<?php

class ArgContext extends CliLib implements ArrayAccess
{

	private $context;
	private $args;

	public function __construct (CliContext $context, array $args)
	{
		$this->context = $context;
		$this->args = $args;
	}

	public function newArgs (array $args)
	{
		return new ArgContext($this->context, $args);
	}

	public function call ($name, array $args = null)
	{
		if ($args === null)
		{
			$args = $this->args;
		}

		$mode = $this->context->getMode();
		$type = $mode->getIdentifierType($name);

		if ($type === false)
		{
			throw new CliIdentifierException("Unknown call identifier '$name'");
		}

		switch ($type)
		{
			case CliMode::ID_FUNCTION:
				$call = new ExpFunction($name, $mode);
				break;

			case CliMode::ID_OPERATOR:
				$call = $mode->getOperatorByName($name);
				break;

			case CliMode::ID_COMMAND:
				$call = new CommandStmt($name, $args, $mode);
				return $call->execute($this->context);

			default:
				throw new CliIdentifierException("Identifier '$name' is not callable");
		}

		return $call->call($this->newArgs($args));
	}

	public function getContext ()
	{
		return $this->context;
	}

	public function subContext ()
	{
		$this->context = $this->context->createChild();
	}

	public function operandCount ($need)
	{
		$has = count($this->args);

		if ($has != $need)
		{
			throw new CliCallException(
				"Unexpected argument count '$has', expected '$need'");
		}

		return true;
	}

	public function argCount ()
	{
		return count($this->args);
	}

	public function requireCount ($min, $max = null)
	{
		if (!$this->hasCount($min, $max))
		{
			throw new CliArgumentException(
				"Invalid argument count '" . count($this->args) . "'");
		}

		return true;
	}

	public function hasCount ($min, $max = null)
	{
		$count = count($this->args);

		if ($max === null)
		{
			return $count == $min;
		}

		return $count >= $min && ($count === false || $count <= $max);
	}

	public function requireArg ($i)
	{
		if (!isset($this->args[$i]))
		{
			throw new CliArgumentException("Missing argument '$i'");
		}

		return true;
	}

	public function requireTypes ()
	{
		$this->requireCount(func_num_args());

		foreach (func_get_args() as $i => $type)
		{
			$this->requireType($i, $type);
		}

		return true;
	}

	public function requireType ($i, $type)
	{
		$this->requireArg($i);

		if (!$this->hasType($i, $type))
		{
			self::invalidType($this->args[$i], $type);
		}

		return true;
	}

	public function hasType ($i, $type)
	{
		return isset($this->args[$i]) && self::isType($this->args[$i], $type);
	}

	public function toNumber ($i)
	{
		$this->requireArg($i);
		return self::numberOf($this->args[$i], $this->context);
	}

	public function toModeNumber($i)
	{
		$this->requireArg($i);
		return self::modeNumberOf($this->args[$i], $this->context);
	}

	public function toBool ($i)
	{
		$this->requireArg($i);
		return self::isTrue($this->args[$i], $this->context);
	}

	public function toValue ($i)
	{
		$this->requireArg($i);
		return self::valueOf($this->args[$i], $this->context);
	}

	public function toString ($i)
	{
		$this->requireArg($i);
		return self::stringOf($this->args[$i], $this->context);
	}

	public function getConf ($name)
	{
		return $this->context->getSetting($name);
	}

	public function setConf ($name, $value)
	{
		$this->context->setSetting($name, $value);
	}

	/* ############### ArrayAccess implementation ############### */

	public function offsetExists ($offset)
	{
		return isset($this->args[$offset]);
	}

	public function offsetGet ($offset)
	{
		$this->requireArg($offset);
		return $this->args[$offset];
	}

	public function offsetSet ($offset, $value)
	{
		throw new CliCallException("Argument values cannot be changed");
	}

	public function offsetUnset ($offset)
	{
		throw new CliCallException("Argument values cannot be unset");
	}

}

?>
